/////////////////////////////////////////////////////////////////////////////
// Name:        ax_menu.h
// Purpose:     Menu classes
// Author:      Julian Smart
// Modified by:
// Created:     2009-03-20
// RCS-ID:
// Copyright:   (c) Julian Smart
// Licence:     New BSD License
/////////////////////////////////////////////////////////////////////////////

#ifndef _AX_MENU_H_
#define _AX_MENU_H_

/*
 * Includes
 */

#include "wx/dynarray.h"

#include "ax/ax_property.h"

/*
 * Forward declarations
 */

class AxMenu;
class AxMenuItem;
class AxMenuItemData;
class AxViewManager;
class AxSpeechEvent;
class AxActivator;
class AxValidator;
class AxMenuController;

WX_DEFINE_ARRAY_PTR(AxMenu*, AxMenuArray);

/** Navigation hint, so speech can indicate how navigation happened.
 */

typedef enum {
    AxNavigationHintNone,
    AxNavigationHintUp,
    AxNavigationHintBack,
    AxNavigationHintHome
} AxNavigationHint;

/**
  Menu item styles
 */

typedef enum {
    AxMenuStyleNormal=0x0000,
    AxMenuStyleBold=0x0001,
    AxMenuStyleItalic=0x0002
};

// Standard menu identifiers
#define AxIdentifierOK          wxT("OK")
#define AxIdentifierCancel      wxT("CANCEL")
#define AxIdentifierGoUp        wxT("GO UP")
#define AxIdentifierYes         wxT("YES")
#define AxIdentifierNo          wxT("NO")
#define AxIdentifierExit        wxT("EXIT")
#define AxIdentifierGoHome      wxT("GO HOME")

// Navigation/speech identifiers for shortcuts

#define AxIdentifierReadCurrentWord         wxT("READCURRENTWORD")
#define AxIdentifierReadNextWord            wxT("READNEXTWORD")
#define AxIdentifierReadPreviousWord        wxT("READPREVIOUSWORD")

#define AxIdentifierReadCurrentSentence     wxT("READCURRENTSENTENCE")
#define AxIdentifierReadNextSentence        wxT("READNEXTSENTENCE")
#define AxIdentifierReadPreviousSentence    wxT("READPREVIOUSSENTENCE")

#define AxIdentifierReadCurrentParagraph    wxT("READCURRENTPARAGRAPH")
#define AxIdentifierReadNextParagraph       wxT("READNEXTPARAGRAPH")
#define AxIdentifierReadPreviousParagraph   wxT("READPREVIOUSPARAGRAPH")

#define AxIdentifierReadFromHere            wxT("READFROMHERE")
#define AxIdentifierReadAll                 wxT("READALL")

#define AxIdentifierDescribeContainerWindow wxT("DESCRIBECONTAINERWINDOW")
#define AxIdentifierDescribeContext         wxT("DESCRIBECONTEXT")
#define AxIdentifierDescribeItem            wxT("DESCRIBEITEM")
#define AxIdentifierStopSpeech              wxT("STOPSPEECH")
#define AxIdentifierPauseResumeSpeech       wxT("PAUSERESUMESPEECH")
#define AxIdentifierSkipForward             wxT("SKIPFORWARD")
#define AxIdentifierSkipBack                wxT("SKIPBACK")

#define AxIdentifierContextHelp             wxT("CONTEXTHELP")

/// Gets an integer equivalent identifier, using XRCID
#define AXID(id) AxGetId(wxT(id))

int AxGetId(const wxChar* id);

/**
    @class AxMenuItem

    This class represents a menu item in an AxMenu. It has a label, a description,
    and a string identifier. It can be disabled or enabled, and it can contain a submenu.

    If the menu item has an AxActivator object associated with it, AxActivator::Activate
    is called when the item is activated, for example by the user double-clicking on the
    item or pressing Return. Similarly, selecting the menu will cause AxActivator::Select
    to be called. AxActivator::Activate can return a submenu, which will be shown
    if non-NULL.

    A menu item can be marked dynamic with AxMenuItem::SetDynamic. A dynamic item
    can have its submenu deleted by the system when not showing, and it also
    ensures that old information is not used - the submenu will be recreated every time
    it is invoked. If your item is static, be sure not to recreate its submenu every time -
    return AxMenItem::GetSubMenu after the first AxMenuItem::Activate call.

    A menu item can contain an AxValidator object. AxValidator classes implement
    TransferDataFromMenu and TransferDataToMenu functions that get or set data
    using data objects specific to the AxValidator class. For example, the
    AxTextValidator class implemented in @c ax_menu_text.h works with the
    AxTextMenuItem class. AxTextMenuItem stores a string that can be changed
    on activation; and without a validator, the text string will remain in the
    text menu item. But when a AxTextValidator is specified, created with a pointer
    to a wxString, the wxString value will be set when the user enters a new value.

    Note that validator and activator objects should not be shared between different
    menu items. Always create new objects. Also, ensure that menu items can be copied
    correctly by implementing the Clone function for any derived AxMenuItem, AxActivator
    and AxValidator classes.

    @category{ax}

 */

class AxMenuItem: public wxEvtHandler
{
    DECLARE_DYNAMIC_CLASS( AxMenuItem )

public:
// Constructors

    AxMenuItem(const AxMenuItem& item) { Init(); Copy(item); }
    AxMenuItem() { Init(); }

    /// Construct a menu item with label and description, and optional identifier and
    /// activator object.
    AxMenuItem(const wxString& label, const wxString& descr,
               const wxString& id = wxEmptyString, AxActivator* activator = NULL)
    { Init(); m_label = label; m_description = descr; m_activator = activator; m_id = id; }
    ~AxMenuItem();

// Operations

    /// Member initialisation
    void Init();

    void Copy(const AxMenuItem& item);
    void operator=(const AxMenuItem& item) { Copy(item); }
    bool operator==(const AxMenuItem& item) const ;

    /// Override this function for derived classes.
    virtual AxMenuItem* Clone() const { return new AxMenuItem(*this); }

    /// Sets the submenu.
    void SetSubMenu(AxMenu* menu);

    /// Gets the submenu.
    AxMenu* GetSubMenu() const { return m_subMenu; }

    /// Sets the activator object. The menu item will now own the object.
    void SetActivator(AxActivator* activator);

    /// Gets the activator object.
    AxActivator* GetActivator() const { return m_activator; }

    /// Sets the validator object. The menu item will now own the object.
    void SetValidator(AxValidator* validator);

    /// Gets the validator object.
    AxValidator* GetValidator() const { return m_validator; }

    /// Sets the data associated with the menu item. This is not currently
    /// used and will probably be removed.
    void SetData(AxMenuItemData* data) { m_data = data; }

    /// Gets the data associated with the menu item. This is not currently
    /// used and will probably be removed.
    AxMenuItemData* GetData() const { return m_data; }

    /// Sets the string identifier associated with the menu item.
    /// Translate it to a wxWidgets integer identifier with the AxId macro
    /// or AxGetId function.
    void SetId(const wxString& id) { m_id = id; }

    /// Gets the string identifier associated with the menu item.
    const wxString& GetId() const { return m_id; }

    /// Sets the menu item style. This is a bit list of the following values:
    ///
    /// @li AxMenuStyleNormal
    /// @li AxMenuStyleBold
    /// @li AxMenuStyleItalic

    void SetStyle(int style) { m_style = style; }

    /// Gets the menu item style.
    int GetStyle() const { return m_style; }

    /// Enables or disables the item. The item will be displayed with a grey
    /// label, and activation will not be possible.
    void Enable(bool enable = true) { m_enabled = enable; }

    /// Returns true if the item is enabled.
    bool IsEnabled() const { return m_enabled; }

    /// Sets the descriptive text. This will appear beneath the menu control,
    /// in the standard user interface.
    void SetDescription(const wxString& description) { m_description = description; }

    /// Gets the descriptive text.
    const wxString& GetDescription() const { return m_description; }

    /// Sets the label.
    void SetLabel(const wxString& label) { m_label = label; }

    /// Gets the label.
    const wxString& GetLabel() const { return m_label; }

    /// Sets the parent menu.
    void SetParent(AxMenu* menu) { m_parent = menu; }

    /// Gets the parent menu.
    AxMenu* GetParent() const { return m_parent; }

    /// Marks the item as dynamic (the submenu should be recreated each time it is
    /// used). A dynamic menu item should return
    /// a new submenu every time its associated AxActivator::Activate function is called.

    void SetDynamic(bool dynamic = true) { m_dynamic = dynamic; }

    /// Returns true if the item is dynamic.
    bool IsDynamic() const { return m_dynamic; }

    /// Returns true if the item is static.
    bool IsStatic() const { return !IsDynamic(); }

    /// Get the zero-based position in the parent menu.
    int GetPosition() const;

    /// Processes the event, or sends it up the hierarchy.
    virtual bool ProcessEvent(wxEvent& event);

    /// Transfer data using validator, if any.
    virtual bool TransferDataToMenu(bool recurse = false);

    /// Transfer data using validator, if any.
    virtual bool TransferDataFromMenu(bool recurse = false);

    /// Set menu item label spec, if any. This will be used by
    /// menu item classes that support keywords in labels and descriptions.
    void SetLabelSpec(const wxString& spec);

    /// Gets menu item label spec, if any
    wxString GetLabelSpec() const;

    /// Sets menu item description spec, if any.
    /// This will be used by menu item classes that support keywords in labels and descriptions.
    void SetDescriptionSpec(const wxString& spec);

    /// Gets menu item description spec, if any
    wxString GetDescriptionSpec() const;

    /// Optionally provide a short name used to describe the item.
    /// By default, this is empty, in order not to clutter speech unnecessarily.
    /// But you might want to provide it to indicate the kind of
    /// operation the user might expect, without needing to describe it in detail.
    /// For example, a toggle item sets the kind to "toggle".
    void SetKind(const wxString& kind) { m_kind = kind; }
    
    /// Returns the kind of menu item, such as command, file selector, etc.
    const wxString& GetKind() const { return m_kind; }

protected:

    AxMenu*         m_subMenu;
    AxMenuItemData* m_data;
    wxString        m_label;
    wxString        m_kind;
    wxString        m_id;
    wxString        m_description;
    AxMenu*         m_parent;
    AxActivator*    m_activator;
    AxValidator*    m_validator;
    int             m_style;
    bool            m_enabled;
    bool            m_dynamic;
};

WX_DEFINE_ARRAY_PTR(AxMenuItem*, AxMenuItemArray);

/**
    @class AxGoUpMenuItem

    Use this class if you wish to add a Go Up menu item to a menu.

    @category{ax}

 */

class AxGoUpMenuItem: public AxMenuItem
{
    DECLARE_DYNAMIC_CLASS( AxGoUpMenuItem )

public:
// Constructors

    AxGoUpMenuItem(const AxGoUpMenuItem& item) { Copy(item); }
    AxGoUpMenuItem();
};

/**
    @class AxMenu

    This class represents a menu containing one or more menu items.

    @category{ax}

 */

class AxMenu: public wxEvtHandler
{
    DECLARE_DYNAMIC_CLASS( AxMenu )

public:
// Constructors

    AxMenu(const AxMenu& menu) { Init(); Copy(menu); }
    AxMenu(const wxString& label = wxEmptyString, const wxString& descr = wxEmptyString)
    { Init(); m_label = label; m_description = descr; }
    ~AxMenu();

// Operations

    /// Member initialisation
    void Init();

    /// Get items.
    AxMenuItemArray& GetItems() { return m_items; }

    /// Gets the number of items in the menu.
    size_t GetCount() const { return m_items.GetCount(); }

    /// Gets the menu item at position @a i (zero-based).
    AxMenuItem* GetItem(size_t i) const { return m_items.Item(i); }

    /// Copies the menu.
    void Copy(const AxMenu& menu);
    void operator=(const AxMenu& menu) { Copy(menu); }
    bool operator==(const AxMenu& menu) const ;

    /// Sets the menu identifier. Not currently used.
    void SetId(const wxString& id) { m_id = id; }

    /// Gets the menu identifier.
    const wxString& GetId() const { return m_id; }

    /// Sets the menu description, which will appear below the menu control in the standard user interface.
    void SetDescription(const wxString& description) { m_description = description; }

    /// Gets the menu description, which will appear below the menu control in the standard user interface.
    const wxString& GetDescription() const { return m_description; }

    /// Sets the menu label, which will appear above the menu control in the standard user interface.
    void SetLabel(const wxString& label) { m_label = label; }

    /// Gets the menu label, which will appear above the menu control in the standard user interface.
    const wxString& GetLabel() const { return m_label; }

    /// Sets the parent menu item.
    void SetParent(AxMenuItem* item) { m_parent = item; }

    /// Gets the parent menu item.
    AxMenuItem* GetParent() const { return m_parent; }

    /// Adds an item to the menu and sets the item's parent.
    void AddItem(AxMenuItem* item);

    /// Removes an item from the menu, without deleting the item.
    void RemoveChild(AxMenuItem* item);

    /// Removes an item from the menu and deletes it.
    void DeleteChild(AxMenuItem* item);

    /// Unlinks the menu from its associated item.
    void Unlink();

    /// Deletes all the children of the menu.
    void ClearChildren();

    /// Finds a menu item by identifier.
    AxMenuItem* FindItemById(const wxString& id) const;

    /// Finds a menu item by label.
    AxMenuItem* FindItemByLabel(const wxString& label) const;

    /// Gets the current zero-based menu selection.
    int GetCurrentSelection() const { return m_currentSelection; }

    /// Sets the current zero-based menu selection.
    void SetCurrentSelection(int sel) { m_currentSelection = sel; }

    /// Gets the current menu item.
    AxMenuItem* GetCurrentItem() const;

    /// Returns true if this menu is showing modally.
    bool IsModal() const { return m_isModal; }

    /// Marks the menu as showing modally.
    void SetModal(bool isModal) { m_isModal = isModal; }

    /// Performs delayed menu deletion.
    static void DoDelayedMenuDeletion();

    /// Schedules a menu for delayed deletion.
    static void ScheduleMenuForDeletion(AxMenu* menu);

    /// Transfer data using validator, if any.
    virtual bool TransferDataToMenu(bool recurse = false);

    /// Transfer data using validator, if any.
    virtual bool TransferDataFromMenu(bool recurse = false);

protected:

    AxMenuItemArray m_items;

    wxString        m_label;
    wxString        m_id;
    wxString        m_description;
    AxMenuItem*     m_parent;
    int             m_currentSelection;
    bool            m_isModal;

    static AxMenuArray  sm_menusPendingDeletion;
};

/**
    @class AxMenuItemData

    Derive from this class to store specific, clonable data within a menu item.
    This is not currently used and may be removed in future.

    @category{ax}

 */

class AxMenuItemData: public wxObject
{
    DECLARE_CLASS( AxMenuItemData )

public:
// Constructors

    AxMenuItemData() { Init(); }
    ~AxMenuItemData() {}

// Operations

    /// Member initialisation
    void Init() {}

    virtual AxMenuItemData* Clone() const = 0;

protected:
};

/**
    @class AxMenuController

    This class coordinates activity between
    the AxMenu, the control used to display it, and
    the view manager.

    Derive from this class and override functions that indicate when the menu
    should change. Also, call functions in this object to indicate activation, etc.

    @category{ax}

 */

class AxMenuController: public wxEvtHandler
{
    DECLARE_CLASS( AxMenuController )

public:
// Constructors

    AxMenuController() { Init(); }
    ~AxMenuController();

// Operations

    void Init();

    /// Set/get view manager
    void SetViewManager(AxViewManager* manager) { m_viewManager = manager; }
    AxViewManager* GetViewManager() const { return m_viewManager; }

    /// Get the main menu
    AxMenu* GetMenu() const { return m_menu; }

    /// Set the main menu
    void SetMenu(AxMenu* menu);

    /// Get current menu (either the top-level menu, or a submenu) 
    AxMenu* GetCurrentMenu() const { return m_currentMenu; }

    /// Set current menu (either the top-level menu, or a submenu) 
    void SetCurrentMenu(AxMenu* menu) { m_currentMenu = menu; }

    /// Show initial top-level menu
    virtual bool ShowInitialMenu(AxMenu* menu);

    /// Called by the menu control when the user has interacted with it
    virtual bool Activate(AxMenuItem* item);
    virtual bool Select(AxMenuItem* item);
    virtual bool Describe(AxMenuItem* item, int describeAtVerbosity = 5);
    virtual bool DescribeCurrentContext(int describeAtVerbosity = 3);
    virtual bool DescribeCurrentItem(int describeAtVerbosity = 5);
    virtual bool GoUp();
    virtual bool GoHome();

    /// Show menu, optionally passing the item it's spawned from.
    /// If childMenu is specified, link 'menu' but then show the child menu
    /// immediately (i.e., a child of the menu we're showing).
    virtual bool ShowMenu(AxMenu* menu, AxMenuItem* parentItem = NULL, AxMenu* childMenu = NULL,
        AxNavigationHint hint = AxNavigationHintNone);

    /// Update the current menu UI
    virtual bool UpdateUI(AxMenu* menu);

    /// Returns true if the user can go up a menu
    virtual bool CanGoUp() const;

    /// Returns true if the user can go to the home menu
    virtual bool CanGoHome() const;

    /// Called when the menu must be refreshed - implement for a specific control
    virtual bool RefreshMenu(AxMenu* menu, AxNavigationHint hint) = 0;

    /// Called when a menu item should be selected - implement for a specific control
    virtual bool SetMenuSelection(AxMenuItem* menuItem) = 0;

    /// Called when a menu item must be updated
    virtual bool UpdateMenuItem(AxMenuItem* item) = 0;

    /// Called when we need to get the currently selected menu item - implement for a specific control
    virtual AxMenuItem* GetMenuSelection() const = 0;

    /// Called when the menu becomes modal
    virtual bool BeginModal() = 0;

    /// Called when the menu stops being modal
    virtual bool EndModal() = 0;

    /// Called when the current view title should be changed - implement for a specific title control
    virtual bool SetTitle(const wxString& title) = 0;

    /// Called when the menu should get the focus.
    virtual bool SetMenuFocus() = 0;

    /// Override if something needs to be done before the menu context changes, e.g.
    /// saving an edit. If this returns false, it vetoes the menu change.
    virtual bool OnChangeMenuPre(AxMenu* currentMenu, AxMenu* newMenu);

    /// Override if something needs to be done before the menu context changes, e.g.
    /// saving an edit. If this returns false, it vetoes the menu change.
    virtual bool OnChangeMenuPost(AxMenu* newMenu);

    /// Override to do something just before the menu is refreshed.
    virtual bool OnBeforeDisplay(AxMenu* newMenu);

    /// Override to update the appearance when settings change.
    virtual bool UpdateAppearance(int hints) = 0;

    /// Emit speech
    bool EmitSpeech(AxSpeechEvent& event) { return DoEmitSpeech(event); }

    /// Emit speech
    bool EmitSpeech(const wxString& speechEventType, const wxString& text = wxEmptyString,
        AxNavigationHint hint = AxNavigationHintNone, int flags = 0);

    /// Emit speech
    bool EmitSpeech(const wxString& speechEventType, AxNavigationHint hint, int flags = 0);

    /// Speech emission implementation.
    virtual bool DoEmitSpeech(AxSpeechEvent& event);

    /// Perform idle processing
    virtual bool IdleProcessing();

protected:
    AxMenu*             m_menu;
    AxMenu*             m_currentMenu;
    AxViewManager*      m_viewManager;
};

#endif
    // _AX_MENU_H_
